#include <stdio.h> // Для printf
#include <stdlib.h>
#include <string.h>
#include <getopt.h> 
#include <unistd.h> 
#include <pthread.h> 
#include <arpa/inet.h>
#include <time.h> 
#include <netdb.h> 

#include <network.h> 
#include <server.h>  // (¯◡◡¯) 

#define LOGFILE "log"


void addClient(int, struct sockaddr_in);
void exitNow(char *);
void scanArgs(int argc, char *[]);
void networkInitialization();
void otherInitialization();
void listening();

/*									*/
struct sockaddr_in serveraddr;
struct hostent *server;
int port = 2012;
int sockfd = 0;
int idClient = 0;
int reserve = -1;
int maxid = 0;
FILE *logfile;
time_t howtime;
/*									*/
/*	Номер порта по умолчанию 2012   */

void exitNow(char *msg){ // Выводит сообщение и выходит.
	if (msg != NULL){
		printf("%s\n", msg);
	}
	if (sockfd > 0){
		close(sockfd);
	}
	exit(0);
}

void scanArgs(int argc, char *argv[]){
	struct option long_options[] = {
					 {"port", required_argument, NULL, 'p'},
 					 {"server", required_argument, NULL, 's'},
  					 {0, 0, 0, 0} 
				     };
	char arg; 

	while ((arg = getopt_long(argc, argv, "p:s:", long_options, NULL)) != -1){
		switch (arg){
			case 'p':{
				port = atoi(optarg);
				if ((1024 > port) || (port > 65536))
					exitNow("Некорректный порт. Допустимые значения: 1024-65535.");
				break;
			}
			case 's':{ 
				    server = gethostbyname(optarg); // domain -> ip
    				if (server == NULL) {
						exitNow("Ошибка. Неверное имя сервера.");
   					 }
				break;
			}
			default:
				exitNow("[-p|--port Номер порта] [-s| --server Адрес сервера]");
		}
	}
	printf("Порт: %d\n", port);
}

void networkInitialization(){
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
		exitNow("Ошибка при создании сокета.");
	}
	    
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(port);
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
   
	if (bind(sockfd, (struct sockaddr*) &serveraddr, sizeof(serveraddr)) < 0)
		exitNow("Ошибка привязки сокета.");

	if (listen(sockfd, MAXCONNECTION) < 0){
		exitNow("Ошибка прослушмивания сокета.");
	}
}

void otherInitialization(){	
	if ((clients = malloc(sizeof(struct client) * MAXCONNECTION)) == NULL){ // Резервируем память под клиентов
		exitNow("Ошибка. Не достаточно памяти (clients)");
	}
	int i;
	for (i = 0; i < MAXCONNECTION; i++){
		clients[i].id = -1;
		clients[i].sockfd = -1;
		clients[i].flag = 0;
		strcpy (clients[i].name, "Anonymouse");
	}
}

void listening(){ 
	time(&howtime);
	fprintf(logfile, "%sServer started. Port = %d.\n", asctime(localtime(&howtime)), port);
	fflush(logfile);
	
	int newSockfd;
	struct sockaddr_in clientaddr;
	socklen_t length = sizeof(clientaddr);
	
	while((newSockfd = accept(sockfd, (struct sockaddr *) &clientaddr, &length)) > 0){
		addClient(newSockfd, clientaddr);
	}
}

void addClient(int clientSockfd, struct sockaddr_in clientAddr){ 
	int i;
	for (i = 0; i < MAXCONNECTION; i++){ // Ищем свободный сокет для вошедшего клиента.
		if (clients[i].sockfd == -1) break;
	}
	if (i == MAXCONNECTION) {
		printf("Какой-то клиент не смог подключиться из-за нехватки мест на сервере.\n");
		sending(clientSockfd, MAXCONNECT, NULL);
		return;
	}
	bzero(&clients[i], sizeof(struct client)); 
	clients[i].id = idClient;
	idClient++;
	clients[i].sockfd = clientSockfd;
	pthread_create(&clients[i].thread, NULL, lobby, (void *) &clients[i]);
	printf("Подключен клиент №%d, ID - %d\n", i+1,  clients[i].id);
}

void openFile(char *path){
	int resize = 0;
	char *temp = strrchr(path,  '/');
	resize = strlen(path) - strlen(temp);
	path[resize+1] = '\0';
	strcat(path, LOGFILE);

	logfile = fopen(path, "a");
	if(logfile == NULL){
		exitNow("Не удалось открыть файл с журналом.");
	}
}

int main(int argc, char *argv[])
{	
	openFile(argv[0]);	
	scanArgs(argc, argv);
    if (server == NULL){
    	otherInitialization();
		networkInitialization();
		printf("Сервер запущен\n");
		listening();
		printf("Сервер отключен\n");
    }
    else{
	    otherInitialization();
    	connection2mainserver();
    	cloning();
    	port++;
    	idClient = maxid + 1;
		networkInitialization();
		 printAllInfo();
		printf("Сервер запущен\n");
		listening();
    }
	return 0; 
}
